package com.ark.service.service.impl;

import com.ark.common.constant.Constants;
import com.ark.common.domain.entity.sys.SysMenu;
import com.ark.common.domain.entity.sys.SysUser;
import com.ark.service.domain.vo.MetaVo;
import com.ark.service.domain.vo.RouterVo;
import com.ark.service.mapper.SysMenuMapper;
import com.ark.service.service.SysMenuService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Resource
    private SysMenuMapper menuMapper;

    @Override
    public List<SysMenu> selectMenusByUserId(Long userId) {
        List<SysMenu> menus;
        if(SysUser.isAdmin(userId)){
            menus = selectAllMenus();
        }else {
            menus = selectMenuTreeByUserId(userId);
        }
        return getChildPerms(menus);
    }

    @Override
    public List<SysMenu> selectMenuList(SysMenu menu) {
        LambdaQueryWrapper<SysMenu> wrapper=new LambdaQueryWrapper<>();
        wrapper.eq(Objects.nonNull(menu.getMenuName()),SysMenu::getMenuName, menu.getMenuName())
                .eq(Objects.nonNull(menu.getStatus()),SysMenu::getStatus, menu.getStatus());
        return list(wrapper);
    }

    @Override
    public List<SysMenu> selectAllMenus() {
        return lambdaQuery()
                .in(SysMenu::getMenuType, "F", "M")
                .eq(SysMenu::getStatus, "0")
                .orderByAsc(SysMenu::getParentId)
                .orderByAsc(SysMenu::getOrderNum)
                .list();
    }

    @Override
    public List<SysMenu> selectMenuTreeByUserId(Long userId) {
        return menuMapper.selectMenuTreeByUserId(userId);
    }

    @Override
    public Set<String> selectMenuPermsByRoleId(Long roleId) {
        List<String> perms = menuMapper.selectMenuPermsByRoleId(roleId);
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms) {
            if (StringUtils.isNotEmpty(perm))
            {
                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
            }
        }
        return permsSet;
    }

    @Override
    public List<RouterVo> buildRouters(List<SysMenu> list) {
        List<RouterVo> routers = new LinkedList<>();
        for (SysMenu menu : list) {
            RouterVo router=new RouterVo();
            router.setName(getRouteName(menu));
            router.setPath(getRoutePath(menu));
            router.setHidden(!menu.getVisible().equals("0"));
            router.setComponent(getComponent(menu));
            router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
            List<SysMenu> children=menu.getChildren();
            if (!CollectionUtils.isEmpty(children) && menu.getMenuType().equals("F")) {
                router.setAlwaysShow(true);
                router.setChildren(buildRouters(children));
            }else if (isMenuFrame(menu)){
                router.setMeta(null);
                List<RouterVo> childrenList = new ArrayList<>();
                RouterVo child = new RouterVo();
                child.setPath(menu.getPath());
                child.setComponent(menu.getComponent());
                child.setName(StringUtils.capitalize(menu.getPath()));
                child.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
                childrenList.add(child);
                router.setChildren(childrenList);
            }else if (menu.getParentId().intValue() != 0 && isInnerLink(menu)){
                router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
                router.setPath("/");
                List<RouterVo> childrenList = new ArrayList<>();
                RouterVo child = new RouterVo();
                String path = innerLinkReplaceEach(menu.getPath());
                child.setPath(path);
                child.setComponent("InnerLink");
                child.setName(StringUtils.capitalize(path));
                child.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
                childrenList.add(child);
                router.setChildren(childrenList);
            }
            routers.add(router);
        }
        return routers;
    }

    @Override
    public Set<String> selectMenuPermsByUserId(Long userId) {
        List<String> perms = menuMapper.selectMenuPermsByUserId(userId);
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms)
        {
            if (StringUtils.isNotEmpty(perm))
            {
                permsSet.addAll(Arrays.asList(perm.trim().split(",")));
            }
        }
        return permsSet;
    }

    private String getComponent(SysMenu menu){
        String component = "Layout";
        if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu))
        {
            component = menu.getComponent();
        }
        else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu))
        {
            component = "InnerLink";
        }
        else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu))
        {
            component = "ParentView";
        }
        return component;
    }

    /**
     * 获取子路由
     * @param list 菜单列表
     */
    public List<SysMenu> getChildPerms(List<SysMenu> list) {
        Map<Long, SysMenu> map = new HashMap<>();
        List<SysMenu> rootList = new ArrayList<>();

        // 第一步：将所有节点放入map中，以便快速查找
        for (SysMenu menu : list) {
            map.put(menu.getId(), menu);
        }

        // 第二步：构建树形结构
        for (SysMenu menu : list) {
            if (menu.getParentId() == 0) {
                rootList.add(menu);
            } else {

                SysMenu parent = map.get(menu.getParentId());
                if (parent != null) {
                    parent.getChildren().add(menu);
                }
            }
        }

        return rootList;
    }

    /**
     * 返回路由名称
     * 当routeName为空则使用path名称
     */
    private String getRouteName(SysMenu menu){
        //非外链并且是一级目录（类型为目录）
        if (isMenuFrame(menu)){
            return StringUtils.EMPTY;
        }
        return getRouteName(menu.getRouteName(),menu.getPath());
    }

    /**
     * 返回路由名称 当routeName为空则使用path名称
     * @param routeName 路由名称
     * @param path 路径
     */
    private String getRouteName(String routeName, String path){
        String name = StringUtils.isNotEmpty(routeName) ? routeName : path;
        return StringUtils.capitalize(name);
    }

    /**
     * 获取路由路径
     */
    private String getRoutePath(SysMenu menu){
        String path=menu.getPath();
        if (menu.getParentId().intValue() != 0 && menu.getIsBacklink().equals("0") && StringUtils.startsWithAny(path, Constants.HTTP, Constants.HTTPS)){
            path = innerLinkReplaceEach(path);
        }
        // 非外链并且是一级目录（类型为目录）
        if (0 == menu.getParentId().intValue() && "F".equals(menu.getMenuType())
                && menu.getIsBacklink().equals("0"))
        {
            path = "/" + menu.getPath();
        }
        // 非外链并且是一级目录（类型为菜单）
        else if (isMenuFrame(menu))
        {
            path = "/";
        }
        return path;
    }

    /**
     *  非外链并且是一级目录（类型为目录）
     */
    private boolean isMenuFrame(SysMenu menu){

        return menu.getParentId().intValue() == 0 && menu.getMenuType().equals("M")
                && menu.getIsBacklink().equals("0");
    }

    /**
     * 内链域名特殊字符替换
     *
     * @return 替换后的内链域名
     */
    public String innerLinkReplaceEach(String path)
    {
        return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":" },
                new String[] { "", "", "", "/", "/" });
    }
    /**
     * 是否为内链组件
     *
     * @param menu 菜单信息
     * @return 结果
     */
    public boolean isInnerLink(SysMenu menu)
    {
        return menu.getIsBacklink().equals("0") && StringUtils.startsWithAny(menu.getPath(), Constants.HTTP, Constants.HTTPS);
    }

    /**
     * 是否为parent_view组件
     *
     * @param menu 菜单信息
     * @return 结果
     */
    public boolean isParentView(SysMenu menu)
    {
        return menu.getParentId().intValue() != 0 && "F".equals(menu.getMenuType());
    }
}
